package service

import (
	"math"
	"strconv"
	"zimuzu_cvn_web_api/config"
	"zimuzu_cvn_web_api/lib/grpcClient"
	"zimuzu_cvn_web_api/model"
	"zimuzu_cvn_web_api/pkg/drive/db"
	"zimuzu_cvn_web_api/pkg/drive/redis"
	. "zimuzu_cvn_web_api/pkg/e"
	"zimuzu_cvn_web_api/pkg/errno"
	"zimuzu_cvn_web_api/service/cache"
)

type UpdateCvnAmountParams struct {
	Uid              int64
	Amount           float64
	FrozenAmount     float64
	Source           int
	RelateId         string
	RelateTable      string
	From             string
	To               string
	Status           int
	TxHash           string
	Block            int64
	BlockConfirmTime int64
}

//初始化
func InitCvnAmount(uid int64) error {
	amountCreateRequest := map[string]string{
		"uid":                   strconv.FormatInt(uid, 10),
		"balance":               "0",
		"frozen_balance":        "0",
		"last_log_sign_string":  "",
		"last_log_sign_version": "",
	}
	amountCreateResponse, err := grpcClient.CvnAmountCreate(amountCreateRequest)
	if err != nil {
		return err
	}
	err = model.InitCvnAmount(uid, amountCreateResponse.SignString, amountCreateResponse.SignVersion)
	return err
}

//修改账户金额，记录日志
//Auth：TXG
func UpdateCvnAmountBalance(conn *db.TxLogger, data UpdateCvnAmountParams) (logId int64, err error) {
	//加锁
	cacheKey := cache.GetCvnAmountLockKey(data.Uid)
	ok, err := redis.RedisSetnx(cacheKey, "1", 60)
	if err != nil || !ok {
		err = errno.SystemBusyError
		return 0, err
	}
	defer redis.UnDelete(cacheKey)
	//校验流水正负号
	if ok = CheckCvnLogSourceSymbol(data.Source, data.Amount, data.FrozenAmount); !ok {
		err = errno.DbDataError
		return 0, err
	}
	//精度处理
	data.Amount = Decimal(data.Amount, COIN_DECIMAL_CVNT)
	data.FrozenAmount = Decimal(data.FrozenAmount, COIN_DECIMAL_CVNT)
	//是否生成流水
	haveLog := true
	if data.Amount == 0 && data.FrozenAmount != 0 {
		haveLog = false
	}
	//判断余额、冻结金额是否足够
	oldAmount, err := model.GetCvnAmount(conn, data.Uid)
	if err != nil {
		err = errno.GetDataErr
		return 0, err
	}
	if oldAmount.Balance+data.Amount < 0 {
		err = errno.WalletNotEnoughERR
		return 0, err
	}
	if oldAmount.FrozenBalance+data.FrozenAmount < 0 {
		err = errno.WalletNotEnoughERR
		return 0, err
	}
	//数据库签名
	ok, err = model.CheckCvnDbSign(data.Uid)
	if err != nil {
		Log("info", "数据库签名错误：", err.Error())
		err = errno.GetDataErr
		return 0, err
	}
	if !ok {
		Log("info", "签名完之后的数据校验有问题，", ok)
		err = errno.DbDataError
		return 0, err
	}
	//获取最后一条日志签名
	lastLogSign, err := model.GetLastCvnLogSign(conn, data.Uid)
	if err != nil {
		err = errno.GetDataErr
		Log("info", "获取最后一条日志签名错误：", err.Error())
		return 0, err
	}
	if haveLog {
		//生成日志签名
		logCreateRequest := map[string]string{
			"uid":                   strconv.FormatInt(data.Uid, 10),
			"amount":                strconv.FormatFloat(data.Amount, 'f', -1, 64),
			"balance":               strconv.FormatFloat(oldAmount.Balance, 'f', -1, 64),
			"relate_id":             data.RelateId,
			"relate_table":          data.RelateTable,
			"from":                  data.From,
			"to":                    data.To,
			"source":                strconv.Itoa(data.Source),
			"last_log_sign_string":  lastLogSign.SignString,
			"last_log_sign_version": lastLogSign.SignVersion,
		}
		logCreateResponse, err := grpcClient.CvnLogCreate(logCreateRequest)
		if err != nil {
			err = errno.GetDataErr
			Log("info", "生成日志签名错误：", err.Error())
			return 0, err
		}
		//插入cvn_log流水
		logData := model.CvnLogDb{
			Uid:              data.Uid,
			Amount:           data.Amount,
			Balance:          oldAmount.Balance,
			RelateId:         data.RelateId,
			RelateTable:      data.RelateTable,
			From:             data.From,
			To:               data.To,
			Source:           data.Source,
			Status:           data.Status,
			TxHash:           data.TxHash,
			Block:            data.Block,
			BlockConfirmTime: data.BlockConfirmTime,
			SignString:       logCreateResponse.SignString,
			SignVersion:      logCreateResponse.SignVersion,
		}
		logId, err = model.AddCvnLog(conn, logData)
		if err != nil {
			err = errno.UpdateDataErr
			return 0, err
		}
		lastLogSign.SignString = logData.SignString
		lastLogSign.SignVersion = logData.SignVersion
	}
	//生成账户签名
	newBalance := Decimal(oldAmount.Balance+data.Amount, COIN_DECIMAL_CVNT)
	newFrozenBalance := Decimal(oldAmount.FrozenBalance+data.FrozenAmount, COIN_DECIMAL_CVNT)
	amountCreateRequest := map[string]string{
		"uid":                   strconv.FormatInt(data.Uid, 10),
		"balance":               strconv.FormatFloat(newBalance, 'f', -1, 64),
		"frozen_balance":        strconv.FormatFloat(newFrozenBalance, 'f', -1, 64),
		"last_log_sign_string":  lastLogSign.SignString,
		"last_log_sign_version": lastLogSign.SignVersion,
	}
	amountCreateResponse, err := grpcClient.CvnAmountCreate(amountCreateRequest)
	if err != nil {
		err = errno.GetDataErr
		Log("info", "生成账户签名错误：", err.Error())
		return 0, err
	}
	//修改账户金额
	amountData := model.UpdateCvnAmountDb{
		Uid:          data.Uid,
		Amount:       data.Amount,
		FrozenAmount: data.FrozenAmount,
		SignString:   amountCreateResponse.SignString,
		SignVersion:  amountCreateResponse.SignVersion,
	}
	err = model.UpdateCvnAmount(conn, amountData)
	if err != nil {
		err = errno.UpdateDataErr
		return 0, err
	}
	//改库后数据校验
	newAmount, err := model.GetCvnAmount(conn, data.Uid)
	if err != nil {
		err = errno.GetDataErr
		Log("info", "改库后数据校验1 错误：", err.Error())
		return 0, err
	}
	newAmountCreateRequest := map[string]string{
		"uid":                   strconv.FormatInt(data.Uid, 10),
		"balance":               strconv.FormatFloat(newAmount.Balance, 'f', -1, 64),
		"frozen_balance":        strconv.FormatFloat(newAmount.FrozenBalance, 'f', -1, 64),
		"last_log_sign_string":  lastLogSign.SignString,
		"last_log_sign_version": lastLogSign.SignVersion,
	}
	newAmountCreateResponse, err := grpcClient.CvnAmountCreate(newAmountCreateRequest)
	if err != nil {
		err = errno.GetDataErr
		Log("info", "改库后数据校验2 错误：", err.Error())
		return 0, err
	}
	if newAmountCreateResponse.SignString != amountCreateResponse.SignString {
		Log("err", data.Uid, ":数据更新后签名不符")
		err = errno.UpdateDataErr
		return 0, err
	}
	return logId, err
}

//转账成功
//Auth：TXG
func CvnTransferPending(logData model.CvnLogDb) bool {
	logData.Status = model.DealStatusCheck
	err := model.UpdateCvnLogStatus(config.C.DB.DB, logData)
	if err != nil {
		return false
	}
	return true
}

//转账成功
//Auth：TXG
func CvnTransferSuccess(logData model.CvnLogDb) bool {
	logData.Status = model.DealStatusSuccess
	//开启事务
	tx, err := config.C.DB.DB.Beginx()
	if err != nil {
		Log("err", err)
		return false
	}
	//修改提现状态
	err = model.UpdateCvnLogStatus(tx, logData)
	if err != nil {
		_ = tx.Rollback()
		return false
	}
	inviterId, err := model.GetInviter(config.C.DB.DB, logData.Uid)
	if err != nil {
		return false
	}
	if inviterId > 0 {
		//获取手续费
		charge, err := model.GetCvnLogCharge(config.C.DB.DB, logData.Id)
		if err != nil {
			return false
		}
		commission := Decimal(math.Abs(charge)*ReturnCommissionRatio, COIN_DECIMAL_CVNT)
		//修改账户
		amountData := UpdateCvnAmountParams{
			Uid:         inviterId,
			Amount:      commission,
			Source:      model.LogSourceRebate,
			RelateId:    strconv.FormatInt(logData.Id, 10),
			RelateTable: TableCvnLog,
		}
		_, err = UpdateCvnAmountBalance(tx, amountData)
		if err != nil {
			_ = tx.Rollback()
			return false
		}
		//增加返佣
		err = model.UpdateCvnInviteLogCvnt(tx, logData.Uid, commission)
		if err != nil {
			_ = tx.Rollback()
			return false
		}
	}
	//提交事务
	if err = tx.Commit(); err != nil {
		return false
	}
	return true
}

//转账失败
//Auth：TXG
func CvnTransferFailed(logData model.CvnLogDb) bool {
	var charge float64
	if logData.Status != model.DealStatusCheck {
		return false
	}
	if logData.Source != model.LogSourceOut && logData.Source != model.LogSourceInnerOut {
		return false
	}
	logData.Status = model.DealStatusFail
	relateId := strconv.FormatInt(logData.Id, 10)
	ok, err := model.ExistCvnLogRelated(config.C.DB.DB, relateId, model.LogSourceTransferFailBack)
	if err != nil {
		return false
	}
	if ok {
		err = model.UpdateCvnLogStatus(config.C.DB.DB, logData)
		if err != nil {
			return false
		}
		return true
	}
	//站外转账获取手续费
	if logData.Source == model.LogSourceOut {
		charge, err = model.GetCvnLogCharge(config.C.DB.DB, logData.Id)
		if err != nil {
			return false
		}
	}
	//开启事务
	tx, err := config.C.DB.DB.Beginx()
	if err != nil {
		Log("err", err)
		return false
	}
	//修改提现状态
	err = model.UpdateCvnLogStatus(tx, logData)
	if err != nil {
		_ = tx.Rollback()
		return false
	}
	//修改账户金额
	amountData := UpdateCvnAmountParams{
		Uid:         logData.Uid,
		Amount:      math.Abs(logData.Amount) + math.Abs(charge),
		Source:      model.LogSourceTransferFailBack,
		RelateId:    strconv.FormatInt(logData.Id, 10),
		RelateTable: TableCvnLog,
	}
	_, err = UpdateCvnAmountBalance(tx, amountData)
	if err != nil {
		_ = tx.Rollback()
		return false
	}
	if err = tx.Commit(); err != nil {
		return false
	}
	return true
}

//内部充值
//Auth：TXG
func SysUpdateCvn(params model.SysCvnParams) (err error) {
	var updateAmountData UpdateCvnAmountParams
	if err = ValidateCode(AdminPhone, SendCodeSceneAdmin, params.Code); err != nil {
		return err
	}
	params.Amount = math.Abs(params.Amount)
	updateAmountData.Uid = params.Uid
	if params.Action == 1 {
		updateAmountData.Amount = params.Amount
		updateAmountData.Source = model.LogSourceSystemIncrease
	} else if params.Action == 2 {
		updateAmountData.Amount = -params.Amount
		updateAmountData.Source = model.LogSourceSystemDeduction
	} else {
		err = errno.ParamsError
		return err
	}
	tx, err := config.C.DB.DB.Beginx()
	if err != nil {
		err = errno.UpdateDataErr
		return err
	}
	_, err = UpdateCvnAmountBalance(tx, updateAmountData)
	if err != nil {
		_ = tx.Rollback()
		return err
	}
	if err = tx.Commit(); err != nil {
		err = errno.UpdateDataErr
		return err
	}
	return nil
}
